/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <unistd.h>
#include "PagPlayerProxy.h"
#include "hilog/log.h"
#include "napi/pag_napi_comPosition.h"
#include "pag/pag.h"
#include <bits/alltypes.h>
#include "pag_napi_pagFile.h"
#include <iostream>
#include <fstream>
#include <filesystem>
#include "PagSurface.h"
#include <native_vsync/native_vsync.h>
#include <unistd.h>
#include <iostream>
#include "napi/PagCompositionProxy.h"
#include <mutex>

using namespace pag;

namespace NativeXComponentSample {
const int32_t STR_DEFAULT_SIZE = 2048;
const int32_t INDEX_0 = 0;
const int32_t INDEX_1 = 1;
const int32_t INDEX_2 = 2;
const int32_t INDEX_3 = 3;
const int32_t INDEX_4 = 4;
const int32_t INDEX_5 = 5;
const int32_t INDEX_6 = 6;
const int32_t PARAM_COUNT_1 = 1;
const int32_t PARAM_COUNT_2 = 2;
const int32_t PARAM_COUNT_3 = 3;
const int32_t PARAM_COUNT_4 = 4;
const int32_t PARAM_COUNT_5 = 5;
const int32_t PARAM_COUNT_6 = 6;
const int32_t TOTAL_PROGRESS = 1000000;
const int32_t TIME_PROCESS = 1000;
OHOS_PAGSuface *g_ohosSurface = nullptr;
const unsigned int LOG_PRINT_DOMAIN = 0XFF00;
void OnVSync(long long timestamp, void *data);
void DealFrameData();
OH_NativeVSync_FrameCallback g_callback = nullptr;
OH_NativeVSync *g_nativeVSync = nullptr;
std::unordered_map<std::string, PagPlayerProxy *> PagPlayerProxy::pagPlayProxyMap_ = {};
std::vector<PagPlayerProxy *> PagPlayerProxy::pagPlayProxyVector_ = {};
static napi_threadsafe_function g_repeatCountCallbackFunc = nullptr;
static napi_threadsafe_function g_curFrameCallbackFunc = nullptr;
static std::mutex g_procMtx;
static bool g_isClockAlive = true;

void OnVSync(long long timestamp, void *data)
{
    std::lock_guard<std::mutex> locker(g_procMtx);
    auto it = PagPlayerProxy::pagPlayProxyVector_.begin();
    for (; it != PagPlayerProxy::pagPlayProxyVector_.end(); it++) {
        PagPlayerProxy *proxy = *it;
        proxy->OnProxyVSync(timestamp, proxy);
    }
}

void DealFrameData()
{
    char name[] = "hello_vsync";
    g_nativeVSync = OH_NativeVSync_Create(name, strlen(name));
    g_callback = OnVSync;
    OH_NativeVSync_RequestFrame(g_nativeVSync, g_callback, nullptr);
}

void SetupSurface(OH_NativeXComponent *component, void *nativeWindow, void *eglContext)
{
    if (g_ohosSurface == nullptr) {
        g_ohosSurface = new OHOS_PAGSuface();
    }
    g_ohosSurface->PAGSurfaceInit(component, nativeWindow, eglContext);
    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
            "OnSurfaceCreatedCB: Unable to get XComponent id");
        return;
    }
    std::string idCompent = idStr;
    PAGSurface *surface = g_ohosSurface->GetPAGSurface(idCompent);
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "INIT",
        "PagPlayerProxy::SetupSurface %{public}s, surface %{public}p", idCompent.c_str(), surface);
    return;
}

PagPlayerProxy::PagPlayerProxy()
{
    g_procMtx.lock();
    pagPlayProxyVector_.push_back(this);
    g_procMtx.unlock();
}

PagPlayerProxy::~PagPlayerProxy()
{
    OH_NativeVSync_Destroy(g_nativeVSync);
}

napi_value PagPlayerProxy::PagInit(napi_env env, napi_callback_info info)
{
    size_t argc = PARAM_COUNT_1;
    napi_value args[INDEX_1] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    char diStr[1024] = {0};
    size_t charLen = 0;
    napi_get_value_string_utf8(env, args[0], diStr, STR_DEFAULT_SIZE, &charLen);
    id_ = diStr;
    
    m_repeatCount = 0;
    m_startFrame = 0;
    m_bRun = E_PAG_PLAY_NULL;

    m_totalFrame = 0;
    m_cur = 0;
    m_fCount = 0.0f;
    m_refCallBackCommon = nullptr;

    m_Pagplayer = new pag::PAGPlayer();
    return nullptr;
}

napi_value PagPlayerProxy::SetSurface(napi_env env, napi_callback_info info)
{
    if (g_ohosSurface != nullptr) {
        PAGSurface *surface = g_ohosSurface->GetPAGSurface(id_);
        if (surface == nullptr) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Prepare", "get ohosSurface failed!!!");
            return nullptr;
        }
        std::shared_ptr<PAGSurface> newSurface(surface);
        if (m_Pagplayer == nullptr) {
            return nullptr;
        }

        m_Pagplayer->setSurface(newSurface);
    }
    return nullptr;
}

napi_value PagPlayerProxy::Prepare(napi_env env, napi_callback_info info)
{
    int64_t mDuration = m_Pagplayer->duration();
    if (mDuration <= 0) {
        m_Pagplayer->flush();
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "INIT", "PagPlayerProxy::Prepare, player duration is 0");
        return nullptr;
    }
    if (m_bRun == E_PAG_PLAY_PLAYING) {
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "INIT", "PagPlayerProxy::Prepare, already playing");
        return nullptr;
    }

    if (m_Pagplayer == nullptr) {
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "INIT", "PagPlayerProxy::Prepare  ERRO");
        return nullptr;
    }
    curRepeatCount = 0;
    m_startTimes = 0;
    m_Pagplayer->prepare();
    if (g_ohosSurface != nullptr) {
        // 总播放时间/us
        m_duration = m_Pagplayer->duration();
        m_bRun = E_PAG_PLAY_PLAYING;
        StateChanged(env, m_bRun);
        m_env = env;
        if (g_nativeVSync == nullptr) {
            DealFrameData();
        } else if (!g_isClockAlive) {
            OH_NativeVSync_RequestFrame(g_nativeVSync, g_callback, this);
        }
    }
    return nullptr;
}

napi_value PagPlayerProxy::SetComposition(napi_env env, napi_callback_info info)
{
    std::shared_ptr<pag::PAGComposition> composition = g_pagCompositionMap_[id_];
    if (composition == nullptr && g_pagFile == nullptr) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "INIT",
                     "PagPlayerProxy::SetComposition g_pagComposition && g_pagFile empty");
        return nullptr;
    }
    if (composition != nullptr) {
        frameRate = composition->frameRate();
        m_Pagplayer->setComposition(composition);
    } else {
        frameRate = g_pagFile->frameRate();
        m_Pagplayer->setComposition(g_pagFile);
    }
    return nullptr;
}

napi_value PagPlayerProxy::Duration(napi_env env, napi_callback_info info)
{
    napi_value result;
    napi_create_bigint_int64(env, m_Pagplayer->duration(), &result);

    return result;
}

napi_value PagPlayerProxy::CurrentFrame(napi_env env, napi_callback_info info)
{
    napi_value result;
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "INIT", "PagPlayerProxy::currentFrame %{public}ld",
                 m_Pagplayer->currentFrame());
    napi_create_bigint_int64(env, m_Pagplayer->currentFrame(), &result);

    return result;
}

napi_value PagPlayerProxy::GetProgress(napi_env env, napi_callback_info info)
{
    napi_value result;
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "INIT", "PagPlayerProxy::GetProgress %{public}f",
                 m_Pagplayer->getProgress());
    napi_create_double(env, m_Pagplayer->getProgress(), &result);

    return result;
}

napi_value PagPlayerProxy::AddPlayerListener(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value args[1] = {nullptr};
    // 获取传入的参数并依次放入参数数组中
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    napi_create_reference(env, args[0], 1, &m_refCallBackCommon);

    return nullptr;
}

napi_value PagPlayerProxy::SetRepeatCount(napi_env env, napi_callback_info info)
{
    napi_value result;
    int32_t repeatCount = 0;
    size_t argc = PARAM_COUNT_1;
    napi_value argv[INDEX_1] = {nullptr};
    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);

    napi_get_value_int32(env, argv[0], &repeatCount);
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "INIT", "PagPlayerProxy::SetRepeatCount %{public}d",\
        repeatCount);
    m_repeatCount = repeatCount;
    return nullptr;
}

napi_value PagPlayerProxy::SetProgress(napi_env env, napi_callback_info info)
{
    napi_value result;
    int32_t frame = 0;
    size_t argc = PARAM_COUNT_1;
    napi_value argv[INDEX_1] = {nullptr};
    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);

    napi_get_value_int32(env, argv[0], &frame);

    m_startFrame = frame;
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "INIT", "PagPlayerProxy::setProgress %{public}d",
                 m_startFrame);
    return nullptr;
}

void PagPlayerProxy::OnProxyVSync(long long timestamp, void *data)
{
    PagPlayerProxy *proxy = reinterpret_cast<PagPlayerProxy *>(data);
    if (proxy->m_bRun != E_PAG_PLAY_PLAYING) {
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnVSync", "Not playing");
        g_isClockAlive = false;
        return;
    }
    g_isClockAlive = true;
    if (g_nativeVSync == nullptr) {
        return;
    }

    if (proxy->m_startTimes <= 0) {
        int32_t total = static_cast<int32_t>(proxy->m_Pagplayer->duration() * proxy->frameRate / TOTAL_PROGRESS);
        float rate = static_cast<float>(proxy->m_startFrame) / total;
        m_startTimes = timestamp - static_cast<long>(rate * proxy->m_duration * TIME_PROCESS);
    }
    if (proxy->isResume) {
        int32_t total = static_cast<int32_t>(proxy->m_Pagplayer->duration() * proxy->frameRate / TOTAL_PROGRESS);
        float rate = static_cast<float>(proxy->m_currentFrame) / total;
        m_startTimes = timestamp - static_cast<long>(rate * proxy->m_duration * TIME_PROCESS);
        proxy->isResume = false;
    }
    float percent = static_cast<float>((timestamp - m_startTimes) / TIME_PROCESS) / static_cast<float>(m_duration);
    m_Pagplayer->setProgress(percent);
    m_Pagplayer->flush();
    proxy->m_currentFrame = proxy->m_Pagplayer->currentFrame();
    napi_call_threadsafe_function(g_curFrameCallbackFunc, nullptr, napi_tsfn_nonblocking);
    if (percent >= 1.0) {
        proxy->m_bRun = E_PAG_PLAY_COMPLETE;
        proxy->m_startTimes = 0; // for loop play test
        proxy->m_startFrame = 0;
        ++proxy->curRepeatCount;
        napi_call_threadsafe_function(g_repeatCountCallbackFunc, nullptr, napi_tsfn_nonblocking);
        if (proxy->m_repeatCount > 0) {
            OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnVSync", "play complete %{public}d", proxy->m_cur);
            if (proxy->curRepeatCount >= proxy->m_repeatCount) {
                proxy->DestoryPlayRes();
                return;
            }
        }
        proxy->m_bRun = E_PAG_PLAY_PLAYING;
    }
    OH_NativeVSync_RequestFrame(g_nativeVSync, g_callback, proxy);
}

napi_value PagPlayerProxy::Pause(napi_env env, napi_callback_info info)
{
    if (m_bRun == E_PAG_PLAY_PLAYING) {
        m_bRun = E_PAG_PLAY_PAUSE;
        StateChanged(env, m_bRun);
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "INIT", "pause play");
    }
    return nullptr;
}

napi_value PagPlayerProxy::Resume(napi_env env, napi_callback_info info)
{
    if (m_bRun != E_PAG_PLAY_PAUSE) {
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "INIT", "m_bRun != E_PAG_PLAY_PAUSE");
        return nullptr;
    }
    m_bRun = E_PAG_PLAY_PLAYING;
    isResume = true;
    StateChanged(env, m_bRun);
    OH_NativeVSync_RequestFrame(g_nativeVSync, g_callback, this);
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "INIT", "comtinue play");
    return nullptr;
}

napi_value PagPlayerProxy::Stop(napi_env env, napi_callback_info info)
{
    if (m_bRun == E_PAG_PLAY_PLAYING || m_bRun == E_PAG_PLAY_PAUSE) {
        m_bRun = E_PAG_PLAY_STOP;
        DestoryPlayRes();
        StateChanged(env, m_bRun);
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "INIT", "stop play");
    } else {
        return nullptr;
    }
    return nullptr;
}

void PagPlayerProxy::DestoryPlayRes()
{
    m_Pagplayer->autoClear();
}

napi_value PagPlayerProxy::GetState(napi_env env, napi_callback_info info)
{
    napi_value result;
    napi_create_int32(env, (int)m_bRun, &result);

    return result;
}

napi_value PagPlayerProxy::SetScaleMode(napi_env env, napi_callback_info info)
{
    size_t argc = PARAM_COUNT_1;
    napi_value args[INDEX_1] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    int scaleMode = 0;
    napi_get_value_int32(env, args[0], &scaleMode);
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Init",
        "pag::PAGPLayer::setScaleMode(scaleMode) %{public}d", scaleMode);
    m_Pagplayer->setScaleMode(scaleMode);
    return nullptr;
}

napi_value PagPlayerProxy::ScaleMode(napi_env env, napi_callback_info info)
{
    napi_value result;
    napi_create_int64(env, m_Pagplayer->scaleMode(), &result);
    return result;
}

napi_value PagPlayerProxy::NativeSetMatrix(napi_env env, napi_callback_info info)
{
    size_t argc = PARAM_COUNT_6;
    napi_value args[INDEX_6] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    double result;
    float conversion[6];
    for (int32_t i = 0; i < INDEX_6; i++) {
        napi_get_value_double(env, args[i], &result);
        float value = (float)result;
        conversion[i] = value;
    }
    Matrix m = {};
    m.setAll(conversion[0], conversion[1], conversion[2], conversion[3], conversion[4], conversion[5], 0, 0, 1);
    m_Pagplayer->setMatrix(m);
    return nullptr;
}

napi_value PagPlayerProxy::SetCacheEnabled(napi_env env, napi_callback_info info)
{
    size_t argc = PARAM_COUNT_1;
    napi_value args[INDEX_1] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    bool value;
    napi_get_value_bool(env, args[0], &value);
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Init", "pag::PAGPLayer::setCacheEnabled(value) %{public}d",
                 value);
    m_Pagplayer->setCacheEnabled(value);
    return nullptr;
}

napi_value PagPlayerProxy::SetCacheScale(napi_env env, napi_callback_info info)
{
    size_t argc = PARAM_COUNT_1;
    napi_value args[INDEX_1] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    double result;
    napi_get_value_double(env, args[0], &result);
    float value = static_cast<float>(result);
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Init", "pag::PAGPLayer::setCacheScale(value) %{public}f",
                 value);
    m_Pagplayer->setCacheScale(value);
    return nullptr;
}

napi_value PagPlayerProxy::SetMaxFrameRate(napi_env env, napi_callback_info info)
{
    size_t argc = PARAM_COUNT_1;
    napi_value args[INDEX_1] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    double result;
    napi_get_value_double(env, args[0], &result);
    float value = static_cast<float>(result);
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Init", "pag::PAGPLayer::setMaxFrameRate(value) %{public}f",
                 value);
    m_Pagplayer->setMaxFrameRate(value);
    return nullptr;
}

napi_value PagPlayerProxy::SetUseDiskCache(napi_env env, napi_callback_info info)
{
    size_t argc = PARAM_COUNT_1;
    napi_value args[INDEX_1] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    bool value;
    napi_get_value_bool(env, args[0], &value);
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Init", "pag::PAGPLayer::setUseDiskCache(value) %{public}d",
                 value);
    m_Pagplayer->setUseDiskCache(value);
    return nullptr;
}

napi_value PagPlayerProxy::SetVideoEnabled(napi_env env, napi_callback_info info)
{
    size_t argc = PARAM_COUNT_1;
    napi_value args[INDEX_1] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    bool value;
    napi_get_value_bool(env, args[0], &value);
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Init", "pag::PAGPLayer::setVideoEnabled(value) %{public}d",
                 value);
    m_Pagplayer->setVideoEnabled(value);
    return nullptr;
}

napi_value PagPlayerProxy::UseDiskCache(napi_env env, napi_callback_info info)
{
    napi_value result;
    napi_get_boolean(env, m_Pagplayer->useDiskCache(), &result);
    return result;
}

napi_value PagPlayerProxy::VideoEnabled(napi_env env, napi_callback_info info)
{
    napi_value result;
    napi_get_boolean(env, m_Pagplayer->videoEnabled(), &result);
    return result;
}

napi_value PagPlayerProxy::MaxFrameRate(napi_env env, napi_callback_info info)
{
    napi_value result;
    float num = m_Pagplayer->maxFrameRate();
    double value = static_cast<double>(num);
    napi_create_double(env, value, &result);
    return result;
}

napi_value PagPlayerProxy::CacheEnabled(napi_env env, napi_callback_info info)
{
    napi_value result;
    napi_get_boolean(env, m_Pagplayer->cacheEnabled(), &result);
    return result;
}

napi_value PagPlayerProxy::CacheScale(napi_env env, napi_callback_info info)
{
    napi_value result;
    napi_get_boolean(env, m_Pagplayer->cacheScale(), &result);
    return result;
}

napi_value PagPlayerProxy::GetComposition(napi_env env, napi_callback_info info)
{
    napi_value jsTextObject;
    std::shared_ptr<pag::PAGComposition> result = m_Pagplayer->getComposition();

    napi_create_object(env, &jsTextObject);
    napi_unwrap(env, jsTextObject, reinterpret_cast<void **>(&result));

    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PAGPlayer", "PAGPlayer get object ok!");
    return jsTextObject;
}

napi_value PagPlayerProxy::GetLayersUnderPoint(napi_env env, napi_callback_info info)
{
    size_t argc = PARAM_COUNT_2;
    napi_value args[INDEX_2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    double value1;
    napi_get_value_double(env, args[0], &value1);
    float surfaceX = float(value1);
    double value2;
    napi_get_value_double(env, args[1], &value2);
    float surfaceY = float(value2);

    std::vector<std::shared_ptr<PAGLayer>> layers = m_Pagplayer->getLayersUnderPoint(surfaceX, surfaceY);

    napi_value jsLayerArray;
    napi_create_array(env, &jsLayerArray);

    for (size_t i = 0; i < layers.size(); ++i) {
        napi_value jsLayerObj;
        napi_create_object(env, &jsLayerObj);
        napi_set_element(env, jsLayerArray, i, jsLayerObj);
    }

    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PAGPlayer", "PAGPlayer get array ok!");
    return jsLayerArray;
}

napi_value PagPlayerProxy::FlushAndFenceSync(napi_env env, napi_callback_info info)
{
    napi_value result;
    napi_get_boolean(env, m_Pagplayer->flush(), &result);
    return result;
}

void PagPlayerProxy::StateChanged(napi_env env, EnumPagPlayState state, bool bSync)
{
    m_bRun = state;

    if (!bSync) {
        return;
    }
    if (m_refCallBackCommon == nullptr) {
        return;
    }
    // 创建一个int，作为ArkTS的入参
    napi_value argv = nullptr;
    napi_create_int32(env, (int)state, &argv);

    // 调用 JavaScript 函数
    napi_value jsFunction;
    napi_get_reference_value(env, m_refCallBackCommon, &jsFunction);

    // 调用传入的callback，并将其结果返回
    auto status = napi_call_function(env, nullptr, jsFunction, 1, &argv, nullptr);
    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "testTag", "ChangeState status=%{public}d", m_bRun);

    OH_LOG_Print(LOG_APP, LOG_INFO, 0, "testTag", "ChangeState ok!!!");
}

napi_value PagPlayerProxy::RegisterRepeatCountCallback(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value argv[1] = {nullptr};
    napi_value thisVar = nullptr;
    void *data = nullptr;
    napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);

    // 参数检查
    if (argc < 1) {
        napi_throw_error(env, "ParameterError", "one param expected");
        return nullptr;
    }
    napi_valuetype valueType = napi_undefined;
    napi_typeof(env, argv[0], &valueType);
    if (valueType != napi_function) {
        napi_throw_error(env, "ParameterError", "function expected");
        return nullptr;
    }

    if (g_repeatCountCallbackFunc != nullptr) {
        napi_throw_error(env, "g_callbackFunc", "g_callbackFunc has inited");
        return nullptr;
    }

    napi_value name;
    napi_create_string_utf8(env, "napi_create_threadsafe_function", NAPI_AUTO_LENGTH, &name);

    napi_create_threadsafe_function(env, argv[0], nullptr, name, 0, 1, nullptr, nullptr, &curRepeatCount,
                                    RepeatCountfuncCallJS, &g_repeatCountCallbackFunc);

    return nullptr;
}

void PagPlayerProxy::RepeatCountfuncCallJS(napi_env env, napi_value js_callback, void *context, void *data)
{
    napi_status status;
    napi_value undefined;
    napi_value argv;
    napi_get_undefined(env, &undefined);
    napi_create_int32(env, *(int32_t *)context, &argv);
    status = napi_call_function(env, undefined, js_callback, 1, &argv, nullptr);
    if (status != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, "TEST NAPI", "napi_call_function failed! status=%{public}d",
                     status);
    }
}

napi_value PagPlayerProxy::RegisterCurrentFrameCallback(napi_env env, napi_callback_info info)
{
    size_t argc = 1;
    napi_value argv[1] = {nullptr};
    napi_value thisVar = nullptr;
    void *data = nullptr;
    napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);

    // 参数检查
    if (argc < 1) {
        napi_throw_error(env, "ParameterError", "one param expected");
        return nullptr;
    }
    napi_valuetype valueType = napi_undefined;
    napi_typeof(env, argv[0], &valueType);
    if (valueType != napi_function) {
        napi_throw_error(env, "ParameterError", "function expected");
        return nullptr;
    }

    if (g_curFrameCallbackFunc != nullptr) {
        napi_throw_error(env, "g_callbackFunc", "g_callbackFunc has inited");
        return nullptr;
    }

    napi_value name;
    napi_create_string_utf8(env, "napi_create_threadsafe_function", NAPI_AUTO_LENGTH, &name);

    napi_create_threadsafe_function(env, argv[0], nullptr, name, 0, 1, nullptr, nullptr, &m_currentFrame,
                                    CurrentFramefuncCallJS, &g_curFrameCallbackFunc);

    return nullptr;
}

void PagPlayerProxy::CurrentFramefuncCallJS(napi_env env, napi_value js_callback, void *context, void *data)
{
    napi_status status;
    napi_value undefined;
    napi_value argv;
    napi_get_undefined(env, &undefined);
    napi_create_int64(env, *(int64_t *)context, &argv);
    status = napi_call_function(env, undefined, js_callback, 1, &argv, nullptr);
    if (status != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, "TEST NAPI", "napi_call_function failed! status=%{public}d",
                     status);
    }
}

napi_value PagPlayerProxy::RemovePlayStateListener(napi_env env, napi_callback_info info)
{
    // 检查 m_refCallBackCommon 是否为 nullptr，避免双重释放
    if (m_refCallBackCommon != nullptr) {
        // 释放之前创建的引用
        napi_status status = napi_delete_reference(env, m_refCallBackCommon);
        if (status != napi_ok) {
            // 处理错误，例如打印日志或抛出异常
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RemovePlayStateListener",
                         "Failed to delete reference: %{public}d", status);
        } else {
            // 清除引用，避免后续使用
            m_refCallBackCommon = nullptr;
            OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RemovePlayStateListener",
                         "RemovePlayerListener ok!!!");
        }
    } else {
        // 监听器可能已经被移除，或者从未被添加
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RemovePlayStateListener", "No listener to remove");
    }
    return nullptr;
}

napi_value PagPlayerProxy::RemoveRepeatCountListener(napi_env env, napi_callback_info info)
{
    if (g_repeatCountCallbackFunc != nullptr) {
        napi_release_threadsafe_function(g_repeatCountCallbackFunc, napi_tsfn_release);
        g_repeatCountCallbackFunc = nullptr;
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RemoveRepeatCountListener",
                     "RemoveRepeatCountListener ok!!!");
    } else {
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RemoveRepeatCountListener",
                     "no RepeatCountListener to Remove!!!");
    }
    return nullptr;
}

napi_value PagPlayerProxy::RemoveCurrentFrameListener(napi_env env, napi_callback_info info)
{
    if (g_curFrameCallbackFunc != nullptr) {
        napi_release_threadsafe_function(g_curFrameCallbackFunc, napi_tsfn_release);
        g_curFrameCallbackFunc = nullptr;
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RemoveCurrentFrameListener",
                     "RemoveCurrentFrameListener ok!!!");
    } else {
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "RemoveCurrentFrameListener",
                     "no CurrentFrameListener to Remove!!!");
    }
    return nullptr;
}

napi_value PagPlayerProxy::FreeCache(napi_env env, napi_callback_info info)
{
    if (g_ohosSurface != nullptr) {
        std::string id = {1};
        PAGSurface *surface = g_ohosSurface->GetPAGSurface(id);
        if (surface == nullptr) {
            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "INIT", "surface empty!!!");
            return nullptr;
        }
        surface->freeCache();
        return nullptr;
    }
}

} // namespace NativeXComponentSample